package com.molichuxing.services.business.service.impl;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import javax.annotation.Resource;

import org.apache.log4j.Logger;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import com.molichuxing.framework.dto.Paged;
import com.molichuxing.framework.dto.TabDto;
import com.molichuxing.framework.exception.BizException;
import com.molichuxing.framework.utils.ObjectCastUtil;
import com.molichuxing.services.business.convertor.Convert;
import com.molichuxing.services.business.dto.request.create.CommercialInsuranceCreateBizDto;
import com.molichuxing.services.business.dto.request.create.CompulsoryInsuranceCreateBizDto;
import com.molichuxing.services.business.dto.request.modify.CarCommercialInsuranceModifyDto;
import com.molichuxing.services.business.dto.request.modify.CarCompulsoryInsuranceModifyDto;
import com.molichuxing.services.business.dto.response.CarInsuranceCommercialDto;
import com.molichuxing.services.business.dto.response.CarInsuranceDetailDto;
import com.molichuxing.services.business.dto.response.CarInsuranceDto;
import com.molichuxing.services.business.service.CarInsuranceBizService;
import com.molichuxing.services.exception.VehicleExceptionCodeEnum;
import com.molichuxing.services.infrastructure.dto.request.create.InsuranceCreateDto;
import com.molichuxing.services.infrastructure.dto.response.CarDto;
import com.molichuxing.services.infrastructure.dto.response.InsuranceCommercialDto;
import com.molichuxing.services.infrastructure.dto.response.InsuranceDto;
import com.molichuxing.services.infrastructure.service.CarService;
import com.molichuxing.services.infrastructure.service.InsuranceCommercialService;
import com.molichuxing.services.infrastructure.service.InsuranceService;
import com.molichuxing.services.property.CarInsuranceStatusEnum;
import com.molichuxing.services.property.CarInsuranceTypeEnum;

/**
 * <p>
 * 车辆保险 服务实现类
 * </p>
 *
 * @author
 * @since 2019-08-15
 */
@Service("compulsoryInsuranceBizService")
public class InsuranceBizServiceImpl implements CarInsuranceBizService {
	private static final Logger logger = Logger.getLogger(InsuranceBizServiceImpl.class);

	private static final Integer ID_SIZE = 10000;

	@Resource
	private InsuranceService insuranceService;

	@Resource
	private InsuranceCommercialService insuranceCommercialService;

	@Resource
	private CarService carService;

	@Override
	public Paged<CarInsuranceDto> getInsurancePage(Map<String, Object> mapParam, Integer pageNum, Integer pageSize) {
		Paged<CarInsuranceDto> paged = new Paged<>();
		paged.setTab(getTabList(Integer.parseInt(mapParam.get("type").toString())));
		if (null != mapParam.get("licencePlate") || mapParam.get("vin") != null) {
			String licencePlate = mapParam.get("licencePlate") == null ? null : mapParam.get("licencePlate").toString();
			String vin = mapParam.get("vin") == null ? null : mapParam.get("vin").toString();
			CarDto carDto = carService.getByVinLicencePlate(vin, licencePlate);
			if (carDto == null) {
				return paged;
			}
			List<Integer> carIds = new ArrayList<>();
			carIds.add(carDto.getId());
			mapParam.put("carIds", carIds);
		}
		Paged<InsuranceDto> result = insuranceService.getInsurancePage(mapParam, pageNum, pageSize);
		if ((null == result) || (null == result.getList())) {
			return paged;
		}
		Map<Integer, InsuranceCommercialDto> insuranceCommercialDtoMap = null;
		if (Integer.parseInt(String.valueOf(mapParam.get("type"))) == CarInsuranceTypeEnum.COMMERCIAL_INSURANCE
				.getValue()) {
			insuranceCommercialDtoMap = insuranceCommercialService.getInsuranceCommercialAmount(
					result.getList().stream().map(InsuranceDto::getId).collect(Collectors.toList()));
		}
		paged.setList(Convert.toCompulsoryInsuranceDto(result.getList(), getCarList(result.getList()),
				insuranceCommercialDtoMap));
		paged.setTotal(result.getTotal());
		paged.setPageSize(pageSize);
		return paged;
	}

	@Override
	public List<CarInsuranceDto> getInsuranceList(Integer vehicleId, CarInsuranceTypeEnum carInsuranceTypeEnum) {
		List<InsuranceDto> insuranceList = insuranceService.getInsuranceByCarId(vehicleId, carInsuranceTypeEnum);
		Map<Integer, InsuranceCommercialDto> insuranceCommercialDtoMap = null;
		if (carInsuranceTypeEnum == CarInsuranceTypeEnum.COMMERCIAL_INSURANCE && null != insuranceList
				&& 0 < insuranceList.size()) {
			insuranceCommercialDtoMap = insuranceCommercialService.getInsuranceCommercialAmount(
					insuranceList.stream().map(InsuranceDto::getId).collect(Collectors.toList()));
		}
		return Convert.toCompulsoryInsuranceDto(insuranceList, null, insuranceCommercialDtoMap);
	}

	@Override
	public List<CarInsuranceCommercialDto> getInsuranceCommercialList(Integer vehicleId) {
		List<InsuranceDto> insuranceList = insuranceService.getInsuranceByCarId(vehicleId,
				CarInsuranceTypeEnum.COMMERCIAL_INSURANCE);
		Map<Integer, InsuranceCommercialDto> insuranceCommercialDtoMap = null;
		if (null != insuranceList && 0 < insuranceList.size()) {
			List<InsuranceCommercialDto> insuranceCommercialDtoList = insuranceCommercialService
					.getInsuranceCommercialList(insuranceList.get(0).getId());
			return Convert.toCarInsuranceCommercialDto(insuranceCommercialDtoList);
		}
		return null;
	}

	@Override
	public CarInsuranceDetailDto getInsuranceById(int insuranceId) {
		InsuranceDto insuranceDto = insuranceService.getCompulsoryInsuranceById(insuranceId);
		if (insuranceDto == null) {
			return null;
		}
		CarDto carDto = carService.getCarById(insuranceDto.getCarId());
		List<InsuranceCommercialDto> insuranceCommercialDtoList = null;
		if (insuranceDto.getType() == CarInsuranceTypeEnum.COMMERCIAL_INSURANCE.getValue()) {
			insuranceCommercialDtoList = insuranceCommercialService.getInsuranceCommercialList(insuranceId);
		}
		return Convert.toCarInsuranceDetailDto(insuranceDto, insuranceCommercialDtoList, carDto);
	}

	@Override
	public Boolean modifyCompulsoryInsurance(CarCompulsoryInsuranceModifyDto modifyDto) throws Exception {
		CarDto carDto = validateStatus(modifyDto.getInsuranceId());
		if (!carDto.getIsHavePlate()) {
			throw new BizException(VehicleExceptionCodeEnum.NOT_LICENCE_PLATE.getCode(),
					VehicleExceptionCodeEnum.NOT_LICENCE_PLATE.getMessage());
		}
		return insuranceService.modifyCompulsoryInsurance(Convert.toInsuranceModifyDto(modifyDto));
	}

	@Override
	public Boolean modifyCommercialInsurance(CarCommercialInsuranceModifyDto modifyDto) throws Exception {
		validateStatus(modifyDto.getInsuranceId());
		Boolean result = insuranceService.modifyCompulsoryInsurance(Convert.toInsuranceModifyDto(modifyDto));
		if (result) {
			insuranceCommercialService.modifyCommercial(modifyDto.getInsuranceId(),
					Convert.toInsuranceCommercialModifyDto(modifyDto.getCommercialList()));
		}
		return result;
	}

	@Override
	@Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT, timeout = 36000, rollbackFor = Exception.class)
	public Boolean createCompulsoryInsurance(CompulsoryInsuranceCreateBizDto createBizDto) {
		InsuranceCreateDto createDto = new InsuranceCreateDto();
		BeanUtils.copyProperties(createBizDto, createDto);
		createDto.setType(CarInsuranceTypeEnum.COMPULSORY_INSURANCE.getValue());
		return insuranceService.createInsurance(createDto);
	}

	@Override
	@Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT, timeout = 36000, rollbackFor = Exception.class)
	public Boolean createCommercialInsurance(CommercialInsuranceCreateBizDto createBizDto) {
		InsuranceCreateDto createDto = new InsuranceCreateDto();
		BeanUtils.copyProperties(createBizDto, createDto);
		createDto.setType(CarInsuranceTypeEnum.COMMERCIAL_INSURANCE.getValue());
		Boolean result = insuranceService.createInsurance(createDto);
		if (result) {
			Boolean commercialResult = insuranceCommercialService.createCommercial(createDto.getId(),
					createBizDto.getCreateDto());
			return commercialResult;
		}
		return false;
	}

	/**
	 * 车辆保险导出count
	 * 
	 * @param mapParam
	 * @return
	 */
	@Override
	public Long getDownloadInsuranceCount(Map<String, Object> mapParam) {
		if (null != mapParam.get("licencePlate") || mapParam.get("vin") != null) {
			String licencePlate = mapParam.get("licencePlate") == null ? null : mapParam.get("licencePlate").toString();
			String vin = mapParam.get("vin") == null ? null : mapParam.get("vin").toString();
			CarDto carDto = carService.getByVinLicencePlate(vin, licencePlate);
			if (carDto == null) {
				return null;
			}
			mapParam.put("carId", carDto.getId());
		}

		return insuranceService.getDownloadInsuranceCount(mapParam);
	}

	/**
	 * 车辆保险导出
	 * 
	 * @param mapParam
	 * @return
	 */
	@Override
	public List<CarInsuranceDto> getDownloadInsuranceList(Map<String, Object> mapParam) {
		List<CarInsuranceDto> result = null;

		if (null != mapParam.get("licencePlate") || mapParam.get("vin") != null) {
			String licencePlate = mapParam.get("licencePlate") == null ? null : mapParam.get("licencePlate").toString();
			String vin = mapParam.get("vin") == null ? null : mapParam.get("vin").toString();
			CarDto carDto = carService.getByVinLicencePlate(vin, licencePlate);
			if (carDto == null) {
				return result;
			}
			mapParam.put("carId", carDto.getId());
		}

		List<InsuranceDto> insuranceDtos = insuranceService.getDownloadInsuranceList(mapParam);
		if (insuranceDtos == null || insuranceDtos.isEmpty()) {
			return result;
		}
		int carInsuranceType = ObjectCastUtil.castInt(mapParam.get("type"));
		int size = insuranceDtos.size();
		int start = 0, end = size > ID_SIZE ? ID_SIZE : size;
		int cycle = size % ID_SIZE > 0 ? size / ID_SIZE + 1 : size / ID_SIZE;
		for (int i = 0; i < cycle; i++) {
			if (carInsuranceType == CarInsuranceTypeEnum.COMPULSORY_INSURANCE.getValue()) {
				// 交强险转换
				result = compulsoryConvert(insuranceDtos, result, start, end);
			} else if (carInsuranceType == CarInsuranceTypeEnum.COMMERCIAL_INSURANCE.getValue()) {
				// 商业险转换
				result = commercialConvert(insuranceDtos, result, start, end);
			}
			start = ID_SIZE;
			end = size > (end + ID_SIZE) ? (end + ID_SIZE) : size;
		}

		return result;
	}

	/**
	 * 商业险转换
	 * 
	 * @param insuranceDtos
	 * @return
	 */
	private List<CarInsuranceDto> commercialConvert(List<InsuranceDto> insuranceDtos, List<CarInsuranceDto> result,
			int start, int end) {
		result = result == null ? new ArrayList<>(insuranceDtos.size()) : result;
		// 车辆ids
		List<Integer> carIds = new ArrayList<>(end - start);
		// 保险ids
		List<Integer> insuranceIds = new ArrayList<>();
		for (int i = start; i < end; i++) {
			InsuranceDto insuranceDto = insuranceDtos.get(i);
			carIds.add(insuranceDto.getCarId());
			insuranceIds.add(insuranceDto.getId());
		}
		// 查询车辆信息
		Map<Integer, CarDto> carMap = carService.getCarByIds(carIds);
		// 查询商业险详情信息
		Map<Integer, List<InsuranceCommercialDto>> commercialMap = insuranceCommercialService
				.getInsuranceCommercialList(insuranceIds);

		for (int i = start; i < end; i++) {
			CarInsuranceDetailDto carInsuranceDto = Convert.toCarInsuranceDetailDto(insuranceDtos.get(i));
			if (carMap != null && !carMap.isEmpty()) {
				CarDto carDto = carMap.get(carInsuranceDto.getCarId());
				// 车架号
				carInsuranceDto.setVin(carDto.getVin());
				// 车牌号
				carInsuranceDto.setLicencePlate(carInsuranceDto.getLicencePlate());
			}
			if (commercialMap != null && !commercialMap.isEmpty()) {
				List<InsuranceCommercialDto> insuranceCommercialDtos = commercialMap
						.get(carInsuranceDto.getInsuranceId());
				carInsuranceDto.setCommercialList(Convert.toCarInsuranceCommercialDto(insuranceCommercialDtos));
			}
			result.add(carInsuranceDto);
		}

		return result;
	}

	/**
	 * 交强险转换
	 * 
	 * @param insuranceDtos
	 * @param result
	 * @param start
	 * @param end
	 * @return
	 */
	private List<CarInsuranceDto> compulsoryConvert(List<InsuranceDto> insuranceDtos, List<CarInsuranceDto> result,
			int start, int end) {
		result = result == null ? new ArrayList<>(insuranceDtos.size()) : result;
		// 车辆ids
		List<Integer> carIds = new ArrayList<>(end - start);
		for (int i = start; i < end; i++) {
			carIds.add(insuranceDtos.get(i).getCarId());
		}
		// 查询车辆信息
		Map<Integer, CarDto> carMap = carService.getCarByIds(carIds);

		for (int i = start; i < end; i++) {
			CarInsuranceDto carInsuranceDto = Convert.toCarInsuranceDto(insuranceDtos.get(i));
			if (carMap != null && !carMap.isEmpty()) {
				CarDto carDto = carMap.get(carInsuranceDto.getCarId());
				// 车架号
				carInsuranceDto.setVin(carDto.getVin());
				// 车牌号
				carInsuranceDto.setLicencePlate(carDto.getLicencePlate());
			}
			result.add(carInsuranceDto);
		}

		return result;
	}

	private Map<Integer, CarDto> getCarList(List<InsuranceDto> insuranceDtoList) {
		Map<Integer, CarDto> map = new HashMap<>(10);
		if ((null != insuranceDtoList) && (insuranceDtoList.size() > 0)) {
			for (InsuranceDto dto : insuranceDtoList) {
				CarDto carDto = carService.getCarById(dto.getCarId());
				map.put(dto.getCarId(), carDto);
			}
		}
		return map;
	}

	private CarDto validateStatus(Integer insuranceId) throws Exception {
		InsuranceDto insuranceDto = insuranceService.getCompulsoryInsuranceById(insuranceId);
		if (null == insuranceDto) {
			throw new BizException(VehicleExceptionCodeEnum.NOT_EXIST.getCode(),
					VehicleExceptionCodeEnum.NOT_EXIST.getMessage());
		}
		CarDto carDto = carService.getCarById(insuranceDto.getCarId());
		if (null == carDto) {
			throw new BizException(VehicleExceptionCodeEnum.NOT_EXIST.getCode(),
					VehicleExceptionCodeEnum.NOT_EXIST.getMessage());
		}
		if (!carDto.getIsInStock()) {
			throw new BizException(VehicleExceptionCodeEnum.NOT_IN_STOCK.getCode(),
					VehicleExceptionCodeEnum.NOT_IN_STOCK.getMessage());
		}
		return carDto;
	}

	private List<TabDto> getTabList(Integer type) {
		List<TabDto> tabList = new ArrayList<>();
		for (CarInsuranceStatusEnum statusEnum : CarInsuranceStatusEnum.values()) {
			int count = insuranceService.getInsuranceTabCount(type, statusEnum.getValue());
			TabDto tabDto = new TabDto();
			tabDto.setCount(count);
			tabDto.setName(statusEnum.getName());
			tabDto.setType(statusEnum.getValue());
			tabList.add(tabDto);
		}
		return tabList;
	}

}
